﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Apache.Cassandra;
using Sandy.Selectors;
using Sandy.Connections;

namespace Sandy
{
    public class SandyClient : IDisposable
    {
        private Connection _connection;
        private string _keyspace;

        private Dictionary<string, SandyFamily> _families = new Dictionary<string, SandyFamily>();
        private bool _isPooledConnection;
        private string _poolName;

        public SandyClient(string host, int port, string keyspace)
        {
            _connection = new Connection(host, port);
            _keyspace = keyspace;
            _isPooledConnection = false;

            Discover();
        }

        public SandyClient(string poolName, string keyspace)
        {
            _connection = PoolService.GetConnection(poolName);
            _keyspace = keyspace;
            _isPooledConnection = true;
            _poolName = poolName;

            Discover();
        }

        private void Discover()
        {
            var ksInfo = Client.describe_keyspace(_keyspace);
            foreach (var familyInfo in ksInfo)
            {
                string name = familyInfo.Key;
                bool isSuper = familyInfo.Value["Type"] == "Super";

                ColumnType ctype = ColumnType.Unsupported;
                ColumnType stype = ColumnType.Unsupported;
                if (isSuper)
                {
                    stype = GetColumnType(familyInfo.Value["CompareWith"]);
                    ctype = GetColumnType(familyInfo.Value["CompareSubcolumnsWith"]);
                }
                else
                {
                    ctype = GetColumnType(familyInfo.Value["CompareWith"]);
                }


                _families.Add(name, new SandyFamily(new FamilyInfo() { IsSuper = isSuper, SupercolType = stype, ColumnType = ctype, Name = name }));

            }
        }

        public string ClusterVersion
        {
            get
            {
                return Client.describe_version();
            }
        }

        private ColumnType GetColumnType(string cassandraType)
        {
            if (cassandraType.EndsWith("TimeUUIDType")) return ColumnType.TimeUuidType;
            if (cassandraType.EndsWith("AsciiType")) return ColumnType.AsciiType;
            if (cassandraType.EndsWith("LongType")) return ColumnType.LongType;
            if (cassandraType.EndsWith("UTF8Type")) return ColumnType.Utf8Type;

            return ColumnType.Unsupported;
        }

        internal Cassandra.Client Client
        {
            get
            {
                return _connection.CassandraClient;
            }
        }

        internal string Keyspace
        {
            get
            {
                return _keyspace;
            }
        }

        public FamilySelector Family(string familyName)
        {
            var family = GetFamily(familyName);
            if (family.IsSuper)
                throw new WrongFamilyTypeException(string.Format("{0} is not a Column family (try using client.SuperFamily)", familyName));

            return new FamilySelector(this, GetFamily(familyName));
        }

        internal SandyFamily GetFamily(string familyName)
        {
            return _families[familyName];
        }

        public SuperFamilySelector SuperFamily(string familyName)
        {
            var family = GetFamily(familyName);
            if (!family.IsSuper)
                throw new WrongFamilyTypeException(string.Format("{0} is not a SuperColumn family (try using client.Family)", familyName));

            return new SuperFamilySelector(this, GetFamily(familyName));
        }

        public MutationTracker GetTracker()
        {
            return new MutationTracker(this);
        }

        #region Implementation of IDisposable

        public void Dispose()
        {
            if (_isPooledConnection)
            {
                PoolService.CloseConnection(_poolName, _connection);
            } else
            {
                _connection.Close();
            }
        }

        #endregion
    }

    public class WrongFamilyTypeException : Exception
    {
        public WrongFamilyTypeException(string message) : base(message)
        {
            //
        }
    }
}
